
// debug
#include "gatts.h"

#include "configure.h"

#include "esp_log.h"
#include "gap_gatts_api.h"
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"

static const char *GATTS_TAG = "GATTS";

extern struct configure configure;

SemaphoreHandle_t xMutex;

uint8_t *get_uuid_u8_pointer(struct gatts_uuid *uuid)
{
	if (uuid->bit_len == 16)
		return (uint8_t *)&uuid->uuid.uuid16;
	if (uuid->bit_len == 32)
		return (uint8_t *)&uuid->uuid.uuid32;
	if (uuid->bit_len == 128)
		return uuid->uuid.uuid128;

	ESP_LOGE(GATTS_TAG, "get_uuid_u8_pointer error: bit_len = %d",
		 uuid->bit_len);
	return NULL;
}

gatts_err_t gatts_init(struct gatts_profile *profile)
{
	esp_err_t ret;

	//初始化互斥量
	xMutex = xSemaphoreCreateMutex();

	//  debug
	print_profile(profile, 0);

	gatts_configure(profile);

	print_configure();

	ret = gap_gatts_register();
	ESP_ERROR_CHECK(ret);

	print_configure();

	return GATTS_OK;
}

gatts_err_t gatts_free()
{
	gap_gatts_unregister();
	gatts_unconfigure();
	return GATTS_OK;
}

bool equal_uuid(const struct gatts_uuid *uuid1, const struct gatts_uuid *uuid2)
{
	if (uuid1->bit_len != uuid2->bit_len)
		return false;

	if (uuid1->uuid.uuid16 != uuid2->uuid.uuid16)
		return false;

	return true;
}

gatts_err_t gatts_get_notify_conditions(struct gatts_profile *profile,
					const struct gatts_uuid *uuid)
{
	int i, j;

	for (i = 0; i < profile->service_num; i++) {
		struct gatts_service *s = &profile->services[i];

		for (j = 0; j < s->char_num; j++) {
			struct gatts_char *c = &s->characteristics[j];

			if (!(equal_uuid(&c->uuid, uuid)))
				continue;
			;

			if (!(c->perm & GATTS_PERM_NOTIFY))
				// 权限未配置
				return GATTS_NOTIFY_PERM_NOCNF;

			// 如果uuid相等，且权限开启，且cccd置位正确
			if (c->cccd & 1)
				// notify就绪
				return GATTS_OK;
		}
	}

	// 找不到对应uuid
	return GATTS_UUID_NOTFOUND;
}

gatts_err_t gatts_get_indicate_conditions(struct gatts_profile *profile,
					  const struct gatts_uuid *uuid)
{
	int i, j;

	for (i = 0; i < profile->service_num; i++) {
		struct gatts_service *s = &profile->services[i];

		for (j = 0; j < s->char_num; j++) {
			struct gatts_char *c = &s->characteristics[j];

			if (!(equal_uuid(&c->uuid, uuid)))
				continue;

			if (!(c->perm & GATTS_PERM_INDICATE))
				// 权限未配置
				return GATTS_INDICATE_PERM_NOCNF;

			// 如果uuid相等，且权限开启，且cccd置位正确
			if (c->cccd & (1 << 1))
				// notify就绪
				return GATTS_OK;
		}
	}

	// 找不到对应uuid
	return GATTS_UUID_NOTFOUND;
}

gatts_err_t gatts_set_value(const struct gatts_uuid *uuid, const uint8_t *value,
			    uint16_t len)
{
	uint16_t handle = 0;
	if (!get_char_handle_by_uuid(uuid, &handle)) {
		return GATTS_UUID_NOTFOUND;
	}

	uint8_t *value_internal =
		configure.attr_db[get_idx_by_handle(handle)].att_desc.value;
	uint16_t *len_internal =
		&configure.attr_db[get_idx_by_handle(handle)].att_desc.length;
	uint16_t max_len_internal =
		configure.attr_db[get_idx_by_handle(handle)].att_desc.max_length;

	if (len > max_len_internal) {
		return GATTS_LENGTH_EXCEEDS_MAXLEN;
	}

	// 获取锁
	xSemaphoreTake(xMutex, portMAX_DELAY);

	*len_internal = len;

	for (int i = 0; i < len; i++) {
		value_internal[i] = value[i];
	}

	// 释放锁
	xSemaphoreGive(xMutex);
	return GATTS_OK;
}

gatts_err_t gatts_get_value(const struct gatts_uuid *uuid, uint8_t *value,
			    uint16_t *len, uint16_t max_len)
{
	uint16_t handle = 0;
	if (!get_char_handle_by_uuid(uuid, &handle)) {
		return GATTS_UUID_NOTFOUND;
	}

	uint8_t *value_internal =
		configure.attr_db[get_idx_by_handle(handle)].att_desc.value;
	uint16_t len_internal =
		configure.attr_db[get_idx_by_handle(handle)].att_desc.length;

	if (max_len < len_internal) {
		return GATTS_LENGTH_EXCEEDS_MAXLEN;
	}

	// 获取锁
	xSemaphoreTake(xMutex, portMAX_DELAY);

	if (len != NULL)
		*len = len_internal;

	for (int i = 0; i < len_internal; i++) {
		value[i] = value_internal[i];
	}

	// 释放锁
	xSemaphoreGive(xMutex);
	return GATTS_OK;
}

/**
 * @brief 发送gatts indicate
 * @param uuid 对应的uuid
 * @param need_confirm 是否需要客户端确认
 * @return 返回错误码
 */
static gatts_err_t _gatts_indicate(const struct gatts_uuid *uuid,
				   bool need_confirm)
{
	uint16_t handle = 0;
	if (!get_char_handle_by_uuid(uuid, &handle)) {
		return GATTS_UUID_NOTFOUND;
	}

	uint8_t send_data[22];
	uint16_t len;
	gatts_err_t ret = gatts_get_value(uuid, send_data, &len, 22);
	if (ret)
		return ret;

	esp_ble_gatts_send_indicate(configure.gatts_if, configure.conn_id,
				    handle, len, send_data, need_confirm);
	return GATTS_OK;
}

gatts_err_t gatts_notify(const struct gatts_uuid *uuid)
{
	return _gatts_indicate(uuid, false);
}

gatts_err_t gatts_indicate(const struct gatts_uuid *uuid)
{
	return _gatts_indicate(uuid, true);
}

// FIXME
void uuid_to_str(const struct gatts_uuid *uuid, char *str, size_t max_size)
{
	if (uuid->bit_len == 16)
		snprintf(str, max_size, "uuid: %04X|%u\n", uuid->uuid.uuid16,
			 16);
	// if (uuid->bit_len == 32)
	// 	snprintf(str, max_size, "uuid: %08X|%u\n", uuid->uuid.uuid32,
	// 		 32);
	// if (uuid->bit_len == 128)
	// 	snprintf(str, max_size, "uuid: %032X|%u\n", uuid->uuid.uuid128,
	// 		 128);
}

// todo
void perm_to_str(const gatts_permission_t perm, char *str, size_t max_size)
{
	if (max_size < 1)
		return;
	str[0] = '\0';
	int cnt = max_size;
	const char *perm_str[] = { "read|", "write|", "write_nr|", "notify|",
				   "indicate|" };
	const char *head = "perm: ";
	strncat(str, head, cnt);
	cnt -= strlen(head);
	for (int i = 0; i < 5; i++) {
		if (perm & (1 << (i + 1))) {
			strncat(str, perm_str[i], cnt);
			cnt -= strlen(perm_str[i]);
		}
	}
}

const char *tab_string = "  ";
void print_tab(uint8_t tab_num)
{
	for (int i = 0; i < tab_num; i++) {
		printf("%s", tab_string);
	}
}

// FIXME
void print_uuid(const struct gatts_uuid *uuid, uint8_t tab_num)
{
	print_tab(tab_num);
	if (uuid->bit_len == 16)
		printf("uuid: %04X|%u\n", uuid->uuid.uuid16, 16);
	// if (uuid->bit_len == 32)
	// 	printf("uuid: %08X|%u\n", uuid->uuid.uuid32, 32);
	// if (uuid->bit_len == 128)
	// 	printf("uuid: %032X|%u\n", uuid->uuid.uuid128, 128);
}

// value转十六进制字符串
// 后续可以加上ASCII吗
void print_value(const uint8_t *value, uint16_t length, uint8_t tab_num)
{
	print_tab(tab_num);
	printf("value: %p", value);
	for (int i = 0; i < length; i++) {
		if (i % 8 == 0) {
			printf("\n");
			print_tab(tab_num);
		}
		printf("%02X ", value[i]);
	}
	printf("\n");
}

void print_perm(const gatts_permission_t perm, uint8_t tab_num)
{
	const char *perm_str[] = { "read", "write_nr", "write", "notify",
				   "indicate" };

	print_tab(tab_num);
	printf("perm: ");
	int flg = 0;
	for (int i = 0; i < 5; i++) {
		if (!(perm & (1 << (i + 1))))
			continue;

		if (flg)
			printf(" | ");
		printf("%s", perm_str[i]);
		flg = 1;
	}
	printf("\n");
}

void print_char(struct gatts_char *c, uint8_t tab_num)
{
	print_uuid(&c->uuid, tab_num);

	print_perm(c->perm, tab_num);

	print_tab(tab_num);
	printf("max_length: %u\n", c->max_length);

	print_tab(tab_num);
	printf("length: %u\n", c->length);

	print_value(c->value, c->length, tab_num);

	print_tab(tab_num);
	printf("cccd: %u\n", c->cccd);
}

void print_service(struct gatts_service *s, uint8_t tab_num)
{
	print_tab(tab_num);
	printf("services: %p\n", s);

	print_uuid(&s->uuid, tab_num);

	for (int i = 0; i < s->char_num; i++) {
		print_char(&s->characteristics[i], tab_num + 1);
		print_tab(tab_num);
		printf("--------\n");
	}
}

void print_profile(struct gatts_profile *profile, uint8_t tab_num)
{
	print_tab(tab_num);
	printf("device_name: %s\n", profile->dev_name);

	for (int i = 0; i < profile->service_num; i++) {
		print_service(&profile->services[i], tab_num + 1);
		print_tab(tab_num);
		printf("--------\n");
	}
	print_tab(tab_num);
	printf("service_num: %u\n", profile->service_num);

	print_tab(tab_num);
	printf("read_cb: %p\n", profile->read_cb);

	print_tab(tab_num);
	printf("write_cb: %p\n", profile->write_cb);
}